package com.joysuch.wwyt.dynamicdata.processor;

import cn.hutool.json.JSONUtil;
import cn.zlg.common.datadump.annotation.DataDumpEntity;
import cn.zlg.common.datadump.bean.DataDumpDef;
import cn.zlg.common.datadump.executor.JPAQueryImportExecutor;
import cn.zlg.common.datadump.service.DataDumpService;
import cn.zlg.common.datadump.util.QueryDslUtils;
import com.google.common.collect.Maps;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.bean.BaseTypeGroupItemBean;
import com.joysuch.wwyt.core.common.util.SpringContextUtils;
import com.joysuch.wwyt.core.entity.BaseTypeGroupItem;
import com.joysuch.wwyt.core.service.BaseTypeGroupItemService;
import com.joysuch.wwyt.dynamicdata.DynamicDataConstants;
import com.joysuch.wwyt.dynamicdata.DynamicParam;
import com.joysuch.wwyt.dynamicdata.entity.BaseDynamicData;
import com.joysuch.wwyt.dynamicdata.entity.BaseDynamicFieldConfig;
import com.joysuch.wwyt.dynamicdata.entity.DictionaryDataSource;
import com.joysuch.wwyt.dynamicdata.enums.DataSourceTypeEnum;
import com.joysuch.wwyt.dynamicdata.service.BaseDynamicDataService;
import com.joysuch.wwyt.dynamicdata.service.BaseDynamicFieldConfigService;
import com.joysuch.wwyt.util.ReflectUtil;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.EntityPathBase;
import com.querydsl.core.types.dsl.SimpleExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

public class DynamicDataImportProcessor extends AbstractDynamicDataProcessor implements IDynamicDataProcessor {

    private static final Logger logger = LoggerFactory.getLogger(AbstractDynamicDataProcessor.class);

    private final BaseDynamicDataService dynamicDataService;
    private final BaseTypeGroupItemService baseTypeGroupItemService;
    private final DataDumpService dataDumpService;
    private final BaseDynamicFieldConfigService dynamicFieldConfigService;
    private final JPAQueryFactory factory;
    private final JPAQueryImportExecutor importExecutor;

    public DynamicDataImportProcessor() {
        this.dynamicDataService = SpringContextUtils.getBean(BaseDynamicDataService.class);
        this.baseTypeGroupItemService = SpringContextUtils.getBean(BaseTypeGroupItemService.class);
        this.dataDumpService = SpringContextUtils.getBean(DataDumpService.class);
        this.dynamicFieldConfigService = SpringContextUtils.getBean(BaseDynamicFieldConfigService.class);
        this.factory = SpringContextUtils.getBean(JPAQueryFactory.class);
        this.importExecutor = SpringContextUtils.getBean(JPAQueryImportExecutor.class);
    }

    @Override
    public Object doProcess(DynamicParam param) {
        String dataDumpHandler = param.getDataDumpHandler();
        MultipartFile file = param.getFile();
        String dynamicDataBizType = dataDumpService.getDynamicDataBizType(dataDumpHandler);
        if (StringUtils.isNotBlank(dynamicDataBizType)) {
            String dynamicDataBizCode = dataDumpService.getDynamicDataBizCode(dataDumpHandler);
            List<BaseDynamicFieldConfig> fieldConfigList = dynamicFieldConfigService.findByBizType(dynamicDataBizType);
            Map<String, String> fieldNameMap = fieldConfigList.stream().collect(Collectors.toMap(BaseDynamicFieldConfig::getFieldDesc, BaseDynamicFieldConfig::getFieldName));
            Map<String, Long> nameIdMap = fieldConfigList.stream().collect(Collectors.toMap(BaseDynamicFieldConfig::getFieldName, BaseDynamicFieldConfig::getId));
            //Map<TypeGroupId,Map<TypeGroupItemName,TypeGroupItemId>>
            Map<Long, Map<String, Long>> groupItemMap = buildGroupItemMap(fieldConfigList);
            Map<Long, Long> fieldIdGroupIdMap = buildFieldIdGroupIdMap(fieldConfigList);
            try {
                Workbook workbook = WorkbookFactory.create(file.getInputStream());
                //只获取第一个sheet
                Sheet sheet = workbook.getSheetAt(0);
                Row firstRow = sheet.getRow(sheet.getFirstRowNum());
                Map<Integer, String> fieldValueMap = new HashMap<>();
                int codeIndex = 0;
                DataFormatter formatter = new DataFormatter();
                for (int j = 0; j <= firstRow.getLastCellNum() - 1; j++) {
                    String cellValue = formatter.formatCellValue(firstRow.getCell(j));
                    if (StringUtils.isNotBlank(cellValue) && fieldNameMap.containsKey(cellValue)) {
                        fieldValueMap.put(j, fieldNameMap.get(cellValue));
                    }
                    if (dynamicDataBizCode.equals(cellValue)) {
                        codeIndex = j;
                    }
                }
                Set<String> bizCodes = new HashSet<>();
                List<BaseDynamicData> dynamicDataList = new ArrayList<>();
                for (int j = 1; j <= sheet.getLastRowNum(); j++) {
                    Row row = sheet.getRow(j);
                    if (row != null) {
                        Cell codeCell = row.getCell(codeIndex);
                        String bizCode = formatter.formatCellValue(codeCell);
                        for (Map.Entry<Integer, String> each : fieldValueMap.entrySet()) {
                            Cell cell = row.getCell(each.getKey());
                            if (cell != null) {
                                String cellValue = formatter.formatCellValue(cell);
                                if (StringUtils.isBlank(cellValue) || Constant.NULL_STR.equals(cellValue)) {
                                    continue;
                                }
                                String value = each.getValue();
                                String fieldValue = dictionaryConvertAndSave(groupItemMap, fieldIdGroupIdMap, nameIdMap.get(value), cellValue);
                                BaseDynamicData dynamicData = BaseDynamicData.builder().fieldValue(fieldValue).bizCode(bizCode).fieldConfigId(nameIdMap.get(value)).fieldName(value).build();
                                dynamicDataList.add(dynamicData);
                                bizCodes.add(bizCode);
                            }
                        }
                    }
                }
                Map<String, Long> codeIdMap = builderCodeIdMap(dataDumpHandler, bizCodes);
                Set<Long> bizIds = new HashSet<>();
                List<BaseDynamicData> filterDataList = new ArrayList<>();
                dynamicDataList.forEach(each -> {
                    Long bizId = codeIdMap.get(each.getBizCode());
                    if (bizId != null) {
                        each.setBizId(bizId);
                        bizIds.add(bizId);
                        filterDataList.add(each);
                    }
                });
                return dynamicDataService.saveOrUpdateDataBatch(filterDataList, bizIds);
            } catch (Exception e) {
                logger.error("WorkbookFactory create error", e);
            }
        }
        return false;
    }

    private Map<String, Long> builderCodeIdMap(String dataDumpHandler, Set<String> bizCodes) throws Exception {
        DataDumpDef dumpDef = dataDumpService.getDataDumpDef(dataDumpHandler);
        DataDumpEntity entityAnnotation = (DataDumpEntity) dumpDef.defClass.getAnnotation(DataDumpEntity.class);
        Class mainEntity = entityAnnotation.entity();
        List<Predicate> conditions = new ArrayList<>();
        EntityPathBase q = QueryDslUtils.findQEntityInstance(entityAnnotation.entity());
        importExecutor.tryAddLogicDeleteFlag(conditions, entityAnnotation, q);
        SimpleExpression codeField = (SimpleExpression) QueryDslUtils.findQPath(mainEntity, entityAnnotation.idField());
        JPAQuery query = (JPAQuery) factory.select(q).from(q).where(codeField.in(bizCodes));
        List list = query.fetch();
        Map<String, Long> codeIdMap = Maps.newHashMap();
        for (Object obj : list) {
            Map<String, Object> ObjKVMap = ReflectUtil.getKeyAndValue(obj);
            Object codeFieldValue = ObjKVMap.get(entityAnnotation.idField());
            Object idFieldValue = ObjKVMap.get(DynamicDataConstants.ID);
            if (codeFieldValue != null && idFieldValue != null) {
                codeIdMap.put(codeFieldValue.toString(), Long.valueOf(idFieldValue.toString()));
            }
        }
        return codeIdMap;
    }

    private String dictionaryConvertAndSave(Map<Long, Map<String, Long>> typeGroupMap, Map<Long, Long> fieldDataSourceMap, Long fieldConfigId, Object fieldValue) {
        String valueStr = fieldValue != null ? fieldValue.toString() : null;
        if (fieldDataSourceMap.containsKey(fieldConfigId)) {
            //If the data dictionary does not exist,add new data.
            Long typeGroupId = fieldDataSourceMap.get(fieldConfigId);
            Map<String, Long> groupItemMap = typeGroupMap.get(typeGroupId);
            Long dictionaryId = groupItemMap.get(valueStr);
            return dictionaryId == null ? saveGroupItem(valueStr, typeGroupId).toString() : dictionaryId.toString();
        }
        return valueStr;
    }

    private Long saveGroupItem(String fieldValue, Long typeGroupId) {
        BaseTypeGroupItemBean bean = new BaseTypeGroupItemBean();
        bean.setName(fieldValue);
        bean.setTypeGroupId(typeGroupId);
        BaseTypeGroupItem typeGroupItem = baseTypeGroupItemService.addItem(bean);
        return typeGroupItem.getId();
    }

    private Map<Long, Long> buildFieldIdGroupIdMap(List<BaseDynamicFieldConfig> fieldConfigList) {
        Map<Long, Long> fieldIdGroupIdMap = new HashMap<>();
        fieldConfigList.forEach(each -> {
            String dataSourceStr = each.getDataSource();
            if (JSONUtil.isJsonObj(dataSourceStr)) {
                if (DataSourceTypeEnum.DICTIONARY.getValue().equals(each.getDataSourceType())) {
                    DictionaryDataSource dataSource = JSONUtil.toBean(dataSourceStr, DictionaryDataSource.class);
                    fieldIdGroupIdMap.put(each.getId(), dataSource.getTypeGroupId());
                }
            }
        });
        return fieldIdGroupIdMap;
    }

    private Map<Long, Map<String, Long>> buildGroupItemMap(List<BaseDynamicFieldConfig> fieldConfigList) {
        Map<Long, Map<String, Long>> typeGroupMap = new HashMap<>();
        fieldConfigList.forEach(each -> {
            String dataSourceStr = each.getDataSource();
            if (JSONUtil.isJsonObj(dataSourceStr)) {
                if (DataSourceTypeEnum.DICTIONARY.getValue().equals(each.getDataSourceType())) {
                    DictionaryDataSource dataSource = JSONUtil.toBean(dataSourceStr, DictionaryDataSource.class);
                    if (!typeGroupMap.containsKey(dataSource.getTypeGroupId())) {
                        List<BaseTypeGroupItem> itemList = baseTypeGroupItemService.findByTypeGroupId(dataSource.getTypeGroupId());
                        //The current data volume of the data dictionary table is less than 300
                        Map<String, Long> groupItemMap = new HashMap<>(itemList.stream().collect(Collectors.toMap(BaseTypeGroupItem::getName, BaseTypeGroupItem::getId)));
                        typeGroupMap.put(dataSource.getTypeGroupId(), groupItemMap);
                    }
                }
            }
        });
        return typeGroupMap;
    }

}
